package config

import (
	"os"
	"path/filepath"
	"regexp"
	"strings"
	"testing"
	"time"

	"kwdb-import/pkg/errors"
)

// TestRequiredRule 测试必填验证规则
func TestRequiredRule(t *testing.T) {
	rule := &RequiredRule{FieldName: "test_field"}
	
	tests := []struct {
		name      string
		value     interface{}
		shouldErr bool
	}{
		{"valid string", "test", false},
		{"empty string", "", true},
		{"whitespace string", "   ", true},
		{"nil value", nil, true},
		{"valid int", 42, false},
		{"zero int", 0, false},
		{"valid int64", int64(42), false},
	}
	
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := rule.Validate(tt.value)
			if tt.shouldErr && err == nil {
				t.Errorf("Expected error for value %v, but got none", tt.value)
			}
			if !tt.shouldErr && err != nil {
				t.Errorf("Unexpected error for value %v: %v", tt.value, err)
			}
		})
	}
}

// TestRangeRule 测试范围验证规则
func TestRangeRule(t *testing.T) {
	rule := &RangeRule{FieldName: "port", Min: 1, Max: 65535}
	
	tests := []struct {
		name      string
		value     interface{}
		shouldErr bool
	}{
		{"valid int", 8080, false},
		{"valid int32", int32(3306), false},
		{"valid int64", int64(5432), false},
		{"valid string number", "8080", false},
		{"too small", 0, true},
		{"too large", 70000, true},
		{"invalid string", "not_a_number", true},
		{"negative", -1, true},
	}
	
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := rule.Validate(tt.value)
			if tt.shouldErr && err == nil {
				t.Errorf("Expected error for value %v, but got none", tt.value)
			}
			if !tt.shouldErr && err != nil {
				t.Errorf("Unexpected error for value %v: %v", tt.value, err)
			}
		})
	}
}

// TestRegexRule 测试正则表达式验证规则
func TestRegexRule(t *testing.T) {
	// 邮箱格式验证
	emailPattern := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
	rule := &RegexRule{
		FieldName: "email",
		Pattern:   emailPattern,
		Message:   "Invalid email format",
	}
	
	tests := []struct {
		name      string
		value     interface{}
		shouldErr bool
	}{
		{"valid email", "test@example.com", false},
		{"invalid email", "invalid-email", true},
		{"empty string", "", true},
		{"non-string", 123, true},
	}
	
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := rule.Validate(tt.value)
			if tt.shouldErr && err == nil {
				t.Errorf("Expected error for value %v, but got none", tt.value)
			}
			if !tt.shouldErr && err != nil {
				t.Errorf("Unexpected error for value %v: %v", tt.value, err)
			}
		})
	}
}

// TestURLRule 测试URL验证规则
func TestURLRule(t *testing.T) {
	rule := &URLRule{
		FieldName: "api_url",
		Schemes:   []string{"http", "https"},
	}
	
	tests := []struct {
		name      string
		value     interface{}
		shouldErr bool
	}{
		{"valid http url", "http://example.com", false},
		{"valid https url", "https://api.example.com/v1", false},
		{"invalid scheme", "ftp://example.com", true},
		{"invalid url", "not-a-url", true},
		{"empty string", "", false}, // 空值应该通过，由RequiredRule处理
		{"non-string", 123, true},
	}
	
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := rule.Validate(tt.value)
			if tt.shouldErr && err == nil {
				t.Errorf("Expected error for value %v, but got none", tt.value)
			}
			if !tt.shouldErr && err != nil {
				t.Errorf("Unexpected error for value %v: %v", tt.value, err)
			}
		})
	}
}

// TestFilePathRule 测试文件路径验证规则
func TestFilePathRule(t *testing.T) {
	// 创建临时目录和文件用于测试
	tempDir := t.TempDir()
	tempFile := filepath.Join(tempDir, "test.txt")
	err := os.WriteFile(tempFile, []byte("test"), 0644)
	if err != nil {
		t.Fatalf("Failed to create temp file: %v", err)
	}
	
	tests := []struct {
		name      string
		rule      *FilePathRule
		value     string
		shouldErr bool
	}{
		{
			name: "existing file",
			rule: &FilePathRule{FieldName: "file", MustExist: true, MustBeFile: true},
			value: tempFile,
			shouldErr: false,
		},
		{
			name: "existing directory",
			rule: &FilePathRule{FieldName: "dir", MustExist: true, MustBeDir: true},
			value: tempDir,
			shouldErr: false,
		},
		{
			name: "non-existing file with MustExist",
			rule: &FilePathRule{FieldName: "file", MustExist: true},
			value: filepath.Join(tempDir, "nonexistent.txt"),
			shouldErr: true,
		},
		{
			name: "file when expecting directory",
			rule: &FilePathRule{FieldName: "dir", MustExist: true, MustBeDir: true},
			value: tempFile,
			shouldErr: true,
		},
		{
			name: "directory when expecting file",
			rule: &FilePathRule{FieldName: "file", MustExist: true, MustBeFile: true},
			value: tempDir,
			shouldErr: true,
		},
	}
	
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := tt.rule.Validate(tt.value)
			if tt.shouldErr && err == nil {
				t.Errorf("Expected error for value %v, but got none", tt.value)
			}
			if !tt.shouldErr && err != nil {
				t.Errorf("Unexpected error for value %v: %v", tt.value, err)
			}
		})
	}
}

// TestFilePathRuleCreateIfMissing 测试文件路径创建功能
func TestFilePathRuleCreateIfMissing(t *testing.T) {
	tempDir := t.TempDir()
	newDir := filepath.Join(tempDir, "new_directory")
	
	rule := &FilePathRule{
		FieldName: "dir",
		MustBeDir: true,
		CreateIfMissing: true,
	}
	
	err := rule.Validate(newDir)
	if err != nil {
		t.Errorf("Unexpected error creating directory: %v", err)
	}
	
	// 验证目录是否被创建
	stat, err := os.Stat(newDir)
	if err != nil {
		t.Errorf("Directory should have been created: %v", err)
	}
	if !stat.IsDir() {
		t.Errorf("Created path should be a directory")
	}
}

// TestDurationRule 测试时间间隔验证规则
func TestDurationRule(t *testing.T) {
	rule := &DurationRule{
		FieldName: "timeout",
		Min:       time.Second,
		Max:       time.Hour,
	}
	
	tests := []struct {
		name      string
		value     interface{}
		shouldErr bool
	}{
		{"valid duration", time.Minute * 5, false},
		{"valid string duration", "30s", false},
		{"too short", time.Millisecond * 500, true},
		{"too long", time.Hour * 2, true},
		{"invalid string", "invalid", true},
		{"non-duration type", 123, true},
	}
	
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := rule.Validate(tt.value)
			if tt.shouldErr && err == nil {
				t.Errorf("Expected error for value %v, but got none", tt.value)
			}
			if !tt.shouldErr && err != nil {
				t.Errorf("Unexpected error for value %v: %v", tt.value, err)
			}
		})
	}
}

// TestEnumRule 测试枚举值验证规则
func TestEnumRule(t *testing.T) {
	rule := &EnumRule{
		FieldName: "log_level",
		AllowedValues: []string{"debug", "info", "warn", "error"},
		CaseInsensitive: true,
	}
	
	tests := []struct {
		name      string
		value     interface{}
		shouldErr bool
	}{
		{"valid lowercase", "debug", false},
		{"valid uppercase", "INFO", false},
		{"valid mixed case", "WaRn", false},
		{"invalid value", "trace", true},
		{"empty string", "", false}, // 空值应该通过
		{"non-string", 123, true},
	}
	
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := rule.Validate(tt.value)
			if tt.shouldErr && err == nil {
				t.Errorf("Expected error for value %v, but got none", tt.value)
			}
			if !tt.shouldErr && err != nil {
				t.Errorf("Unexpected error for value %v: %v", tt.value, err)
			}
		})
	}
}

// TestEnumRuleCaseSensitive 测试大小写敏感的枚举验证
func TestEnumRuleCaseSensitive(t *testing.T) {
	rule := &EnumRule{
		FieldName: "mode",
		AllowedValues: []string{"strict", "loose"},
		CaseInsensitive: false,
	}
	
	tests := []struct {
		name      string
		value     string
		shouldErr bool
	}{
		{"exact match", "strict", false},
		{"wrong case", "STRICT", true},
		{"mixed case", "Strict", true},
	}
	
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := rule.Validate(tt.value)
			if tt.shouldErr && err == nil {
				t.Errorf("Expected error for value %v, but got none", tt.value)
			}
			if !tt.shouldErr && err != nil {
				t.Errorf("Unexpected error for value %v: %v", tt.value, err)
			}
		})
	}
}

// TestValidator 测试验证器
func TestValidator(t *testing.T) {
	validator := NewValidator()
	
	// 添加验证规则
	validator.AddRule("name", &RequiredRule{FieldName: "name"})
	validator.AddRule("age", &RangeRule{FieldName: "age", Min: 0, Max: 150})
	
	// 测试有效值
	err := validator.ValidateField("name", "John")
	if err != nil {
		t.Errorf("Unexpected error for valid name: %v", err)
	}
	
	err = validator.ValidateField("age", 25)
	if err != nil {
		t.Errorf("Unexpected error for valid age: %v", err)
	}
	
	// 测试无效值
	err = validator.ValidateField("name", "")
	if err == nil {
		t.Error("Expected error for empty name")
	}
	
	err = validator.ValidateField("age", 200)
	if err == nil {
		t.Error("Expected error for invalid age")
	}
	
	// 测试不存在的字段
	err = validator.ValidateField("nonexistent", "value")
	if err != nil {
		t.Errorf("Unexpected error for nonexistent field: %v", err)
	}
}

// TestConfigValidator 测试配置验证器
func TestConfigValidator(t *testing.T) {
	validator := NewConfigValidator()
	
	// 创建临时文件用于测试
	tempDir := t.TempDir()
	tempFile := filepath.Join(tempDir, "test.sql")
	err := os.WriteFile(tempFile, []byte("SELECT 1;"), 0644)
	if err != nil {
		t.Fatalf("Failed to create temp file: %v", err)
	}
	
	// 测试有效配置
    validConfig := &Config{
        Host:     "localhost",
        Port:     5432,
        DBName:   "testdb",
        Username: "testuser",
        Password: "testpass",
        InputFile: tempFile,
        LogLevel: "info",
        Timeout:  "30s",
        BatchSize: 1000,
        MaxConnections: 5,
        SSLMode: "require",
        OutputDir: tempDir,
    }
	
	err = validator.ValidateConfig(validConfig)
	if err != nil {
		t.Errorf("Unexpected error for valid config: %v", err)
	}
	
	// 测试无效配置
    invalidConfig := &Config{
        Host:     "", // 空主机名
        Port:     70000, // 无效端口
        DBName:   "", // 空数据库名
        Username: "", // 空用户名
        InputFile: "/nonexistent/file.sql", // 不存在的文件
        LogLevel: "invalid", // 无效日志级别
        Timeout:  "invalid", // 无效超时
        BatchSize: 0, // 无效批处理大小
        MaxConnections: 2000, // 超出范围的连接数
        SSLMode: "invalid", // 无效SSL模式
    }
	
	err = validator.ValidateConfig(invalidConfig)
	if err == nil {
		t.Error("Expected error for invalid config")
	}
}

// TestConfigValidatorNilConfig 测试空配置验证
func TestConfigValidatorNilConfig(t *testing.T) {
	validator := NewConfigValidator()
	
	err := validator.ValidateConfig(nil)
	if err == nil {
		t.Error("Expected error for nil config")
	}
	
	if !strings.Contains(err.Error(), "config cannot be nil") {
		t.Errorf("Expected specific error message, got: %v", err)
	}
}

// TestValidateConnectionString 测试连接字符串验证
func TestValidateConnectionString(t *testing.T) {
	validator := NewConfigValidator()
	
	tests := []struct {
		name      string
		connStr   string
		shouldErr bool
	}{
		{
			name: "valid postgres connection",
			connStr: "postgres://user:pass@localhost:5432/dbname",
			shouldErr: false,
		},
		{
			name: "valid postgresql connection",
			connStr: "postgresql://user:pass@localhost:5432/dbname",
			shouldErr: false,
		},
		{
			name: "empty connection string",
			connStr: "",
			shouldErr: true,
		},
		{
			name: "invalid scheme",
			connStr: "mysql://user:pass@localhost:3306/dbname",
			shouldErr: true,
		},
		{
			name: "missing host",
			connStr: "postgres:///dbname",
			shouldErr: true,
		},
		{
			name: "missing database",
			connStr: "postgres://user:pass@localhost:5432/",
			shouldErr: true,
		},
		{
			name: "invalid format",
			connStr: "not-a-url",
			shouldErr: true,
		},
	}
	
	for _, tt := range tests {
		t.Run(tt.name, func(t *testing.T) {
			err := validator.ValidateConnectionString(tt.connStr)
			if tt.shouldErr && err == nil {
				t.Errorf("Expected error for connection string %q, but got none", tt.connStr)
			}
			if !tt.shouldErr && err != nil {
				t.Errorf("Unexpected error for connection string %q: %v", tt.connStr, err)
			}
		})
	}
}

// TestGetValidationSuggestions 测试验证建议
func TestGetValidationSuggestions(t *testing.T) {
	validator := NewConfigValidator()
	
	// 测试缺少可选配置的情况
    minimalConfig := &Config{
        Host:     "localhost",
        Port:     5432,
        DBName:   "testdb",
        Username: "testuser",
        InputFile: "/path/to/file.sql",
    }
	
	suggestions := validator.GetValidationSuggestions(minimalConfig)
	
	// 应该有多个建议
	if len(suggestions) == 0 {
		t.Error("Expected suggestions for minimal config")
	}
	
	// 检查特定建议
	expectedSuggestions := []string{
		"batch_size",
		"max_connections",
		"timeout",
		"log_level",
		"ssl_mode",
		"output_dir",
	}
	
	for _, expected := range expectedSuggestions {
		found := false
		for _, suggestion := range suggestions {
			if strings.Contains(suggestion, expected) {
				found = true
				break
			}
		}
		if !found {
			t.Errorf("Expected suggestion containing %q, but not found in: %v", expected, suggestions)
		}
	}
	
	// 测试完整配置的情况
    completeConfig := &Config{
        Host:     "localhost",
        Port:     5432,
        DBName:   "testdb",
        Username: "testuser",
        InputFile: "/path/to/file.sql",
        LogLevel: "info",
        Timeout:  "30s",
        BatchSize: 1000,
        MaxConnections: 5,
        SSLMode: "require",
        OutputDir: "/tmp",
    }
	
	suggestions = validator.GetValidationSuggestions(completeConfig)
	
	// 完整配置应该没有建议或很少建议
	if len(suggestions) > 0 {
		t.Logf("Complete config still has suggestions: %v", suggestions)
	}
}

// TestValidationRuleNames 测试验证规则名称
func TestValidationRuleNames(t *testing.T) {
	tests := []struct {
		rule ValidationRule
		expectedName string
	}{
		{&RequiredRule{}, "required"},
		{&RangeRule{}, "range"},
		{&RegexRule{}, "regex"},
		{&URLRule{}, "url"},
		{&FilePathRule{}, "filepath"},
		{&DurationRule{}, "duration"},
		{&EnumRule{}, "enum"},
	}
	
	for _, tt := range tests {
		t.Run(tt.expectedName, func(t *testing.T) {
			if tt.rule.Name() != tt.expectedName {
				t.Errorf("Expected rule name %q, got %q", tt.expectedName, tt.rule.Name())
			}
		})
	}
}

// TestValidationErrorTypes 测试验证错误类型
func TestValidationErrorTypes(t *testing.T) {
	rule := &RequiredRule{FieldName: "test"}
	err := rule.Validate(nil)
	
	if err == nil {
		t.Fatal("Expected validation error")
	}
	
	// 检查是否为KWDBError类型
	if kwdbErr, ok := err.(*errors.KWDBError); ok {
		if !errors.IsValidationError(kwdbErr) {
			t.Error("Expected validation error type")
		}
	} else {
		t.Error("Expected KWDBError type")
	}
}

// TestMultipleValidationRules 测试多个验证规则
func TestMultipleValidationRules(t *testing.T) {
	validator := NewValidator()
	
	// 为同一字段添加多个规则
	validator.AddRule("port", &RequiredRule{FieldName: "port"})
	validator.AddRule("port", &RangeRule{FieldName: "port", Min: 1, Max: 65535})
	
	// 测试通过所有规则的值
	err := validator.ValidateField("port", 8080)
	if err != nil {
		t.Errorf("Unexpected error for valid port: %v", err)
	}
	
	// 测试第一个规则失败
	err = validator.ValidateField("port", nil)
	if err == nil {
		t.Error("Expected error for nil port")
	}
	
	// 测试第二个规则失败
	err = validator.ValidateField("port", 70000)
	if err == nil {
		t.Error("Expected error for out-of-range port")
	}
}

// BenchmarkValidation 验证性能测试
func BenchmarkValidation(b *testing.B) {
	validator := NewConfigValidator()
	
	// 创建临时文件
	tempDir := b.TempDir()
	tempFile := filepath.Join(tempDir, "test.sql")
	err := os.WriteFile(tempFile, []byte("SELECT 1;"), 0644)
	if err != nil {
		b.Fatalf("Failed to create temp file: %v", err)
	}
	
    config := &Config{
        Host:     "localhost",
        Port:     5432,
        DBName:   "testdb",
        Username: "testuser",
        Password: "testpass",
        InputFile: tempFile,
        LogLevel: "info",
        Timeout:  "30s",
        BatchSize: 1000,
        MaxConnections: 5,
        SSLMode: "require",
        OutputDir: tempDir,
    }
	
	b.ResetTimer()
	
	b.Run("ValidateConfig", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			_ = validator.ValidateConfig(config)
		}
	})
	
	b.Run("ValidateConnectionString", func(b *testing.B) {
		connStr := "postgres://user:pass@localhost:5432/dbname"
		for i := 0; i < b.N; i++ {
			_ = validator.ValidateConnectionString(connStr)
		}
	})
	
	b.Run("GetValidationSuggestions", func(b *testing.B) {
		for i := 0; i < b.N; i++ {
			_ = validator.GetValidationSuggestions(config)
		}
	})
}

// BenchmarkValidationRules 验证规则性能测试
func BenchmarkValidationRules(b *testing.B) {
	b.Run("RequiredRule", func(b *testing.B) {
		rule := &RequiredRule{FieldName: "test"}
		for i := 0; i < b.N; i++ {
			_ = rule.Validate("test_value")
		}
	})
	
	b.Run("RangeRule", func(b *testing.B) {
		rule := &RangeRule{FieldName: "port", Min: 1, Max: 65535}
		for i := 0; i < b.N; i++ {
			_ = rule.Validate(8080)
		}
	})
	
	b.Run("RegexRule", func(b *testing.B) {
		pattern := regexp.MustCompile(`^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`)
		rule := &RegexRule{FieldName: "email", Pattern: pattern}
		for i := 0; i < b.N; i++ {
			_ = rule.Validate("test@example.com")
		}
	})
	
	b.Run("EnumRule", func(b *testing.B) {
		rule := &EnumRule{
			FieldName: "level",
			AllowedValues: []string{"debug", "info", "warn", "error"},
			CaseInsensitive: true,
		}
		for i := 0; i < b.N; i++ {
			_ = rule.Validate("info")
		}
	})
}